﻿using System;
using System.Collections.Generic;
using GeneticAlgorithm;

namespace SelectorVE
{
    public class VectorEvaluator : ISelector
    {
        private List<SimulatedConfiguration> _input;
        static readonly Random Random = new Random();

        public List<SimulatedConfiguration> Input
        {
            set { _input = new List<SimulatedConfiguration>(value); }
            private get { return _input; }
        }

        public List<Configuration> Output { get; private set; }

        public int PopulationSize { get; set; }

        public void Run()
        {
            GenerateInputFitnessRates();
            Output = SelectFromInput();
        }

        private List<Configuration> SelectFromInput()
        {
            var candidatesForIpc = new List<SimulatedConfiguration>(Input);
            var selectedForIpc = new List<Configuration>();
            var candidatesForPower = new List<SimulatedConfiguration>(Input);
            var selectedForPower = new List<Configuration>();
            double totalIpcFitness = 1;
            double totalPowerFitness = 1;

            for (int k = 0; k < PopulationSize/2; k++)
            {
                var randIpc = totalIpcFitness*Random.NextDouble();
                double intervalLocation = 0;

                for (int i = 0; i < candidatesForIpc.Count; i++)
                {
                    intervalLocation += candidatesForIpc[i].GetScore("ipc_fitness");
                    if (intervalLocation > randIpc)
                    {
                        selectedForIpc.Add(candidatesForIpc[i]);
                        totalIpcFitness -= candidatesForIpc[i].GetScore("ipc_fitness");
                        candidatesForIpc.RemoveAt(i);
                        break;
                    }
                }

                var randPower = totalPowerFitness*Random.NextDouble();
                intervalLocation = 0;

                for (int i = 0; i < candidatesForPower.Count; i++)
                {
                    intervalLocation += candidatesForPower[i].GetScore("power_fitness");
                    if (intervalLocation > randPower)
                    {
                        selectedForPower.Add(candidatesForPower[i]);
                        totalPowerFitness -= candidatesForPower[i].GetScore("power_fitness");
                        candidatesForPower.RemoveAt(i);
                        break;
                    }
                }
            }

            var result = new List<Configuration>(selectedForIpc);
            result.AddRange(selectedForPower);
            return result;
        }

        private void GenerateInputFitnessRates()
        {
            double totalIpc = 0;
            double totalPower = 0;

            foreach (var simulatedConfiguration in Input)
            {
                totalIpc += simulatedConfiguration.GetScore("ipc");
                totalPower += simulatedConfiguration.GetScore("power");
            }

            foreach (var simulatedConfiguration in Input)
            {
                var ipcFitness = simulatedConfiguration.GetScore("ipc")/totalIpc;
                simulatedConfiguration.AddScore("ipc_fitness", ipcFitness);

                var powerFitness = simulatedConfiguration.GetScore("power")/totalPower;
                simulatedConfiguration.AddScore("power_fitness", powerFitness);
            }
        }
    }
}